// 2025/3/4: 首个版本
// 2025/5/16: 使用ChannelNumber.Video

using System;
using System.Collections.Generic;
using ASEva;
using ASEva.Samples;
using ASEva.Utility;

namespace RemoteConsole
{
    class SecondMosaicGenerator
    {
        public void Reset()
        {
            channelSeconds = new List<SecondProfileSample>[ChannelNumber.Video].Populate(i => []);
            channelMosaicHeights = new HeightInfo[ChannelNumber.Video];
        }

        public void UpdateSecondProfilePack(SecondProfilePack secondProfilePack)
        {
            foreach (var pair in secondProfilePack.Profiles)
            {
                if (pair.Key.StartsWith("video@"))
                {
                    int channel = Convert.ToInt32(pair.Key.Substring(6));
                    if (channel >= 0 && channel < channelSeconds.Length) channelSeconds[channel].Add(pair.Value);
                }
            }
        }

        public SecondMosaicPack[] UpdateManualTriggerSample(ManualTriggerSample mtSample)
        {
            var output = new List<SecondMosaicPack>();
            for (int channel = 0; channel < channelSeconds.Length; channel++)
            {
                var list = channelSeconds[channel];
                if (list.Count == 0) continue;

                int removeCount = 0;
                foreach (var sample in list)
                {
                    var targetTimeline = sample.Timeline + (double)channel / ChannelNumber.Video;
                    if (mtSample.Session < sample.Session) break;
                    else if (mtSample.Session == sample.Session && mtSample.Timeline < targetTimeline) break;

                    CommonImage[] mosaics = new CommonImage[sample.Offsets.Length];
                    for (int i = 0; i < sample.Offsets.Length; i++)
                    {
                        var frameOffset = sample.Offsets[i];
                        var frameTimeline = sample.Timeline - sample.Offset + frameOffset;
                        var frameThumbnail = Agency.GetVideoFrameThumbnail(channel, frameTimeline, 0.001, true);
                        if (frameThumbnail == null) continue;

                        var mosaicHeight = Math.Max(1, Math.Min(12, MosaicWidth * frameThumbnail.Height / frameThumbnail.Width));

                        if (channelMosaicHeights[channel] == null)
                        {
                            var rawSize = Agency.GetVideoRawSize(channel, frameTimeline);
                            if (rawSize == null) continue;

                            var vgaHeight = 640 * rawSize.Value.Height / rawSize.Value.Width;
                            channelMosaicHeights[channel] = new HeightInfo{ MosaicHeight = mosaicHeight, VgaHeight = vgaHeight };
                        }
                        else if (mosaicHeight != channelMosaicHeights[channel].MosaicHeight) continue;

                        mosaics[i] = genMosaic(frameThumbnail, channelMosaicHeights[channel].MosaicHeight);
                    }
                    if (channelMosaicHeights[channel] == null) continue;

                    var dummyMosaic = genDummyMosaic(channelMosaicHeights[channel].MosaicHeight);
                    for (int i = 0; i < mosaics.Length; i++)
                    {
                        if (mosaics[i] == null) mosaics[i] = dummyMosaic;
                    }

                    var pack = new SecondMosaicPack();
                    pack.Channel = channel;
                    pack.Session = sample.Session;
                    pack.Second = sample.Second;
                    pack.MosaicHeight = channelMosaicHeights[channel].MosaicHeight;
                    pack.VgaHeight = channelMosaicHeights[channel].VgaHeight;
                    pack.Offsets = sample.Offsets;
                    pack.Mosaics = mosaics;
                    output.Add(pack);
                    
                    removeCount++;
                }

                list.RemoveRange(0, removeCount);
            }
            return output.ToArray();
        }

        private CommonImage genMosaic(CommonImage thumbnail, int mosaicHeight)
        {
            var image = CommonImage.Create(MosaicWidth, mosaicHeight, false);
            int widthStep = thumbnail.Width / MosaicWidth;
            float heightStep = thumbnail.Height / mosaicHeight;
            byte[] thumbnailData = thumbnail.Data;
            for (int v = 0; v < mosaicHeight; v++)
            {
                int v1 = (int)(v * heightStep);
                int v2 = (int)((v + 1) * heightStep);
                int pixelCount = (v2 - v1) * widthStep;
                for (int u = 0; u < MosaicWidth; u++)
                {
                    int bSum = 0, gSum = 0, rSum = 0;
                    int u1 = u * widthStep;
                    int u2 = (u + 1) * widthStep;
                    for (int vSrc = v1; vSrc < v2; vSrc++)
                    {
                        int rowIndex = thumbnail.RowBytes * vSrc;
                        for (int uSrc = u1; uSrc < u2; uSrc++)
                        {
                            int cellIndex = rowIndex + 4 * uSrc;
                            bSum += thumbnailData[cellIndex];
                            gSum += thumbnailData[cellIndex + 1];
                            rSum += thumbnailData[cellIndex + 2];
                        }
                    }
                    image.Data[image.RowBytes * v + 3 * u] = (byte)(bSum / pixelCount);
                    image.Data[image.RowBytes * v + 3 * u + 1] = (byte)(gSum / pixelCount);
                    image.Data[image.RowBytes * v + 3 * u + 2] = (byte)(rSum / pixelCount);
                }
            }

            return image;
        }

        private CommonImage genDummyMosaic(int mosaicHeight)
        {
            var image = CommonImage.Create(MosaicWidth, mosaicHeight, false);
            for (int i = 0; i < image.Data.Length; i++) image.Data[i] = 128;
            return image;
        }

        private class HeightInfo
        {
            public int MosaicHeight { get; set; }
            public int VgaHeight { get; set; }
        };

        private List<SecondProfileSample>[] channelSeconds = new List<SecondProfileSample>[ChannelNumber.Video].Populate(i => []);
        private HeightInfo[] channelMosaicHeights = new HeightInfo[ChannelNumber.Video];

        public const int MosaicWidth = 8;
    }

    class SecondMosaicPack
    {
        public int Channel { get; set; }
        public SessionIdentifier Session { get; set; }
        public int Second { get; set; }
        public int MosaicHeight { get; set; }
        public int VgaHeight { get; set; }
        public double[] Offsets { get; set; }
        public CommonImage[] Mosaics { get; set; }
    }
}